using System;
using System.Data;
using System.Data.Linq;
using System.Data.SqlClient;
using PropertyExpression.Common;

namespace PropertyExpression.ReactiveRelation.Test.Common
{
    public class DataContextProvider<TDataContext> where TDataContext : DataContext
    {
        private readonly Func<IDbConnection, TDataContext> dataContextFunc;
        private readonly Func<string> connectionStringFunc;

        public DataContextProvider(Func<IDbConnection, TDataContext> dataContextFunc, Func<string> connectionStringFunc)
        {
            this.dataContextFunc = dataContextFunc;
            this.connectionStringFunc = connectionStringFunc;
        }

        public TResult Execute<TResult>(Func<TDataContext, TResult> func, IsolationLevel isolationLevel)
        {
            TResult result;
            using (var connection = new SqlConnection(connectionStringFunc()))
            {
                connection.Open();
                var transaction = connection.BeginTransaction(isolationLevel);
                try
                {
                    using (var dataContext = dataContextFunc(connection))
                    {
                        dataContext.Transaction = transaction;

                        result = func(dataContext);
                    }
                    transaction.Commit();
                }
                catch (Exception exception)
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch
                    {
                        throw new ApplicationException("The transaction can not be rolled back.", exception);
                    }
                    throw;
                }
            }
            return result;
        }

        public void Execute(Action<TDataContext> action, IsolationLevel isolationLevel)
        {
            Execute(action.ToFunc(), isolationLevel);
        }
    }
}